2

在 PHP 和 JavaScript 中(或许还有其他语言),变量内所保存的值分为 基本类型值引用类型值

$obj = new stdClass;

若一个变量是一个对象,那么该变量保存的就是一个引用类型的值,即变量中实际保存的是堆内存中对象的地址,而不是对象的实体;若变量为其他类型,则保存的是基本类型值,而不是引用地址。这一点需要特别注意,因为我们可能会遇到如下几种情况,不清楚原理可能导致出错。

赋值

$obj_1 = new stdClass;
$obj_2 = $obj_1;
$obj_1->name = 'Xavier';

var_dump($obj_1->name, $obj_2->name);
var_dump($obj_1, $obj_2);

输出:

string(6) "Xavier"

string(6) "Xavier"

object(stdClass)#1 (1) {
  ["name"]=>
  string(6) "Xavier"
}

object(stdClass)#1 (1) {
  ["name"]=>
  string(6) "Xavier"
}

我们发现 obj_2name 也发生的改变,原因是 obj_1obj_2 指向同一个对象 #1 ,因为在第二行中,我们将 obj_1 所指向的对象的地址赋给了 obj_2

传递参数

我们来看这段代码:

function setName($obj) {
    $obj->name = 'Xavier';
}

$person = new stdClass;
setName($person);
var_dump($person->name); // 输出 string(6) "Xavier"

若变量为一个对象,那么当它作为参数传递给一个函数时,同样,传递的是一个对象地址,而不是拷贝了一个新的对象实体给参数 $obj 。这样,函数内部并没有 return 新的东西出来但改变了外部的状态 的这种情况就变得好理解了。

接下来,请看这段代码:

function setName($obj) {
    $obj->name = 'Xavier';
    $obj = new stdClass;
    $obj->name = 'Zhao';
}

$person = new stdClass;
setName($person);
var_dump($person->name); // 输出了 string(6) "Xavier" 而不是 string(6) "Zhao"

最后的输出结果可能会让很多人会疑惑,他们的思维可能是这样的:

  1. 我将 person 对象的引用地址传递给 obj参数
  2. 在函数内部第一行,根据 obj 的引用地址,我将函数外部对象的 name 属性设置成了 "Xavier"
  3. 在第二行,我将一个新的对象赋给了 obj ,既然 obj 为函数外部对象的引用,那么外部对象也一定变为了这个新的对象
  4. 然后我再给这个新的对象设置新的 name 属性 "zhao" ,嗯,这样外部对象的 name 一定也变成了 "zhao"

如果你想的和上方相同,那可就大错特错了,原因在于对传递参数的过程的错误理解。

首先,我们应该明白,将一个变量作为参数传递给函数 可以理解为 将那个变量保存的值 复制一份给 函数的参数(参数即函数范围的局部变量) 。当函数执行时,外部变量函数参数(局部变量) 是同时存在于内存中的,并且两者是相互独立的,虽然两者所保存的值是相同的;函数参数(局部变量) 会在函数执行完毕后被销毁。

明白了上述原理,那么我们重新来看那段代码:

  1. 函数的 obj 变量在函数内第一行保存的是函数外部 person 变量所保存的值,也就是外部对象的地址
  2. 但到了第二行,obj 变量所保存的值变成了新创建的对象的地址,obj 的指向发生了改变,而原来的外部对象依旧存在并且只被 person 一个变量引用,所以在第三行的行为并没有影响到 person 所指向的那个对象。新创建的对象在函数执行之后被销毁。

所以有代码中注释的输出结果。


Xavier
448 声望28 粉丝

最近的关注重心: